package com.safla.socket;

import org.springframework.stereotype.Component;


import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.*;
/*浏览器：localhost:8080/index.html*/

/**
 * 聊天室服务类，用于实现WebSocket通信，支持用户连接、消息广播、私信发送等功能
 *
 * @ServerEndpoint("/chatRoomServer") :这个注解就是专门标记一个类为websocket服务端点，
 * /ChatRoomServer:服务端点的URI(统一资源标识符) 也就是说，客户端可以通过该uri与服务器创建连接，从而实现通信
 * @Component ：将一个普通类标记成spring上下文中的一个组件,将其加入spring器中
 */
@ServerEndpoint("/chatRoomServer")
@Component
public class ChatRoomServer {

    //数据怎么存储？ mysql（未学）-->创建一个集合存储数据session
    /**
     * sessions:存储所有客户端会话的集合
     * 在Java中 session通常是指程序最用于会话跟踪的一种机制，它是服务端作储用户数据的一种方式，
     * 允许服务器在用户和网站之间多次交互时保持对用户状态的跟踪。
     * Collections.synchronizedSet:保证服务端的线程安全
     * static final :声明一个常量
     * Set<>：用set集合来存储信息
     * Session:会话跟踪机制
     * new HashSet<>()创建一个hashset集合，用于存储消息
     */
    private static final Set<Session> sessions = Collections.synchronizedSet(new HashSet<>());

    /**
     * 存储用户ID与对应会话的映射关系
     * 通过Map集合，可快速根据用户ID获取对应的Session对象，便于实现私信发送等功能。
     */
    private static final Map<String, Session> userSessionMap = new HashMap<>();


    /**
     * 当有新的客户端连接时调用该方法
     *
     * @param session 新连接的客户端会话对象
     */
    @OnOpen
    public void OnOpen(Session session) {
        sessions.add(session);
        //创建用户id
        String userID = session.getId();
        //将用户ID与会话建立映射关系
        userSessionMap.put(userID, session);
        //广播消息，通知所有用户有新用户加入
        broadcastMessage("系统消息：用户 " + userID + "进入了聊天室");
        //更新在线用户列表
        updateOnLineUserList();
    }

    /**
     * 接收客户端发送的消息时调用当前注解的方法
     *
     * @param message 客户端发送的消息内容
     * @param session 发送消息的客户端会话对象
     */
    @OnMessage
    public void onMessage(String message, Session session) {
        //打印会话信息，用于调试
        System.out.println(session);
        //假设当前格式    目标用户id：消息的内容    用于私信
        /*if (message.contains(":")) {    //检查消息是否包含冒号，用于判断是否为私信
            String[] parts = message.split(":");
            //获取目标用户ID
            String toUserID = parts[0];
            //获取消息内容
            String msg = parts[1];
            //发送私信
            sendPrivateMessage(toUserID, "用户" + session.getId() + "私信" + msg);
        } else {*/
            //发送公共消息
            broadcastMessage("用户" + session.getId() + "：" + message);
    }

    /**
     * 当客户端关闭连接时调用此方法
     * 从sessions集合和userSessionMap映射中移除对应的会话信息，
     * 并广播系统消息通知其他用户该用户已退出聊天室。
     *
     * @param session 关闭连接的客户端会话对象
     */
    @OnClose
    public void onClose(Session session) {
        //从sessions集合中移除关闭的会话
        sessions.remove(session);
        //用于存储用户ID
        String userID = null;
        //遍历userSessionMap映射，找到对应的用户ID
        for (Map.Entry<String, Session> entry : userSessionMap.entrySet()) {
            //如果当前会话与关闭的会话相同
            if (entry.getValue().equals(session)) {
                userID = entry.getKey();    //获取用户ID
                break;  //退出循环
            }
        }
        //如果找到了用户ID
        if (userID != null) {
            //从userSessionMap映射中移除该用户
            userSessionMap.remove(userID);
        }
        //广播系统消息
        broadcastMessage("系统消息：用户" + userID + "退出了聊天室");
        //更新在线人数
        updateOnLineUserList();
    }

    /**
     * 广播消息给所有已连接的客户端
     *
     * @param message 要广播的消息内容
     */
    private void broadcastMessage(String message) {

        /*
         * 循环集合中的每一个元素
         * sessions 要被循坏的集合
         * Session:集合中元素的数据类型
         * session:集合中的每个元素
         */
        for (Session session : sessions) {
            try {
                if (session.isOpen()) {
                    session.getBasicRemote().sendText(message);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * updateOnLineUserList:更新在线人数
     */
    private void updateOnLineUserList() {
        //获取所有在线用户的ID
        Set<String> onlineUsers = userSessionMap.keySet();
        //String.join()是一个java中的静态方法，需要在多个字符串组成的一个具有‘，’分隔符发字符串
        String join = String.join(",", onlineUsers);
        System.out.println(join);
        //构建在线用户列表消息
        String onLineUserListMessage = "当前在线的用户：" + String.join(",", onlineUsers);
        //广播在线用户列表消息
        broadcastMessage(onLineUserListMessage);
    }

   /* /**
     * 向指定用户发送私信
     *
     * @param toUserId 接收私信的用户ID
     * @param message  私信内容
     *//*
    private void sendPrivateMessage(String toUserId, String message) {
        //根据用户ID获取对应的会话
        Session toSession = userSessionMap.get(toUserId);
        if (toSession != null) {
            try {
                //向指定用户发送私信
                if (toSession.isOpen()) {
                    toSession.getBasicRemote().sendText(message);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }*/
}
